#include "NetUtils.h"
#include "spdlog/spdlog.h"

namespace LiteNetLib::NetUtils
{

    size_t SockHashCode(asio::ip::udp::endpoint *endpoint)
    {
        size_t hash = 0;

        // Hash the IP address
        const asio::ip::address& ip = endpoint->address();
        if (ip.is_v4())
        {
            const asio::ip::address_v4::bytes_type& ipBytes = ip.to_v4().to_bytes();
            for (size_t i = 0; i < ipBytes.size(); ++i)
            {
                hash ^= static_cast<size_t>(ipBytes[i]) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
            }
        }
        else if (ip.is_v6())
        {
            const asio::ip::address_v6::bytes_type& ipBytes = ip.to_v6().to_bytes();
            for (size_t i = 0; i < ipBytes.size(); ++i)
            {
                hash ^= static_cast<size_t>(ipBytes[i]) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
            }
        }

        // Hash the port number
        uint16_t port = endpoint->port();
        hash ^= static_cast<size_t>(port) + 0x9e3779b9 + (hash << 6) + (hash >> 2);

        return hash;
    }

    std::string GetAddressStr(net_endpoint *endpoint)
    {
        std::ostringstream tmp_os;
        asio::ip::address address = endpoint->address();

        tmp_os.imbue(std::locale::classic());
        if (address.is_v4())
        {
            tmp_os << address;
        }
        else
        {
            tmp_os << '[' << address << ']';
        }
        tmp_os << ':' << endpoint->port();

        return tmp_os.str();
    }

    std::string GetSockIP(net_endpoint *endpoint)
    {
        return endpoint->address().to_string();
    }

    uint64_t ByteToUInt64(const char *buff)
    {
        uint64_t value;
        memcpy(&value, buff, sizeof(value));
        return value;
    }

    int64_t ByteToInt64(const char *buff)
    {
        int64_t value;
        memcpy(&value, buff, sizeof(value));
        return value;
    }

    uint32_t ByteToUInt32(const char *buff)
    {
        uint32_t value;
        memcpy(&value, buff, sizeof(value));
        return value;
    }

    int32_t ByteToInt32(const char *buff)
    {
        int32_t value;
        memcpy(&value, buff, sizeof(value));
        return value;
    }

    uint16_t ByteToUInt16(const char *buff)
    {
        uint16_t value;
        memcpy(&value, buff, sizeof(value));
        return value;
    }

    int16_t ByteToInt16(const char *buff)
    {
        int16_t value;
        memcpy(&value, buff, sizeof(value));
        return value;
    }

    void UInt64ToByte(char *buff, uint64_t value)
    {
        memcpy(buff, &value, sizeof(value));
    }

    void Int64ToByte(char *buff, int64_t value)
    {
        memcpy(buff, &value, sizeof(value));
    }

    void UInt32ToByte(char *buff, uint32_t value)
    {
        memcpy(buff, &value, sizeof(value));
    }

    void Int32ToByte(char *buff, int32_t value)
    {
        memcpy(buff, &value, sizeof(value));
    }

    void UInt16ToByte(char *buff, uint16_t value)
    {
        memcpy(buff, &value, sizeof(value));
    }

    void Int16ToByte(char *buff, int16_t value)
    {
        memcpy(buff, &value, sizeof(value));
    }

    std::string ToHexString(const void* ptr, uint32_t length)
    {
        static char *hexStringCatch = nullptr;
        static uint32_t hexCatchSize = 0; 

        if(hexCatchSize < length * 3)
        {
            delete[] hexStringCatch;
            int size = length * 3 + 128;
            hexStringCatch = new char[size];
            hexCatchSize = size;
        }
        
        for(int index = 0; index < length; index++)
        {
            sprintf(&(hexStringCatch[index * 3]),"%02X ",((const unsigned char*)ptr)[index]);
        }
        hexStringCatch[length * 3 - 1] = '\0';
        return hexStringCatch;
    }

}
